
#include <SFML/System.hpp>
#include <window/window.hpp>
#include <resource_cache/resource_cache.hpp>
#include <messenger/messenger.hpp>
#include <game_state/menu/menu_game_state.hpp>
#include <game_state/running/game_running.hpp>

#include <Input/InputSingleton.h>

#include <physics/collision_message/collision_message.hpp>
#include <nmn/create_string.hpp>

#if ( defined(_DEBUG) || defined(DEBUG) )
#define CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#endif

int main( int argc, char** argv  )
{
	#if ( defined(_DEBUG) || defined(DEBUG) )
	_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
	//_CrtSetBreakAlloc(345);
	#endif

	InputSingleton::createInstance();

	create_window( 1000, 800, 32, "projector" );

	game_state_ptr game_state;

	if ( argc > 1 )
	{
		try
		{
			game_state.reset( new game_running_state( argv[1] ) );
		}
		catch ( ... )
		{
			game_state.reset( new menu_game_state( ) );
		}
	}
	else
	{
		game_state.reset( new menu_game_state( ) );
	}

	//g_messenger.add_listener( "collision_message", []( std::string const & m, message_data_ptr d )
	//{
	//	collision_message * cd = static_cast<collision_message *>( d.get( ) );
	//	std::string out = nmn::create_string( "%1% : %2%\n", m, cd->m_which );
	//	OutputDebugStringA( out.c_str( ) );
	//} );
	
	sf::Clock time;
	float const seconds_per_frame = 1.0f / 200.0f;
	float const approx_vsync_time = 1.0f / 60.0f;
	float const max_updates_per_draw = ( approx_vsync_time / seconds_per_frame ) + 1.0f;
	float accumilated_time = 0.0f;

	while( g_window->IsOpened( ) )
	{
		poll_events( );

		accumilated_time += time.GetElapsedTime( );
		accumilated_time = std::min( accumilated_time, max_updates_per_draw * seconds_per_frame );
		time.Reset( );
		for ( ; accumilated_time >= seconds_per_frame; accumilated_time -= seconds_per_frame )
		{
			//	dispatch all messages in the messenger.
			g_messenger.poll( );

			//	update game state
			game_state = game_state->step( seconds_per_frame );

			g_user_input.update();	// Move it here so sequential updates get different input states.
									// E.g. So two updates in a row don't get "key just pressed".
		}

		g_window->Clear( );

		//	draw game state
		game_state->draw( );

		g_window->Display( );
	}

	game_state.reset( );

	g_cache.unload_images( );

	InputSingleton::deleteInstance();

	destroy_window( );

	return 0;
}
